﻿/* ***********************************************
 * Author		:  kingthy
 * Email		:  kingthy@gmail.com
 * Description	:  DbCommandWrapped
 *
 * ***********************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using System.Data;
using KT.Core.Extensions;
using KT.Framework;
namespace KT.Framework.Database
{
    /// <summary>
    /// 对DbCommand进行包装
    /// </summary>
    public class DbCommandWrapped : IDisposable
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="keepConnection"></param>
        internal DbCommandWrapped(DbCommand command
            , bool keepConnection)
        {
            this.Command = command;
            this.KeepConnection = keepConnection;
            this.ConnectionIsOpened = command.Connection.State != ConnectionState.Closed;
        }
        /// <summary>
        /// 执行命令
        /// </summary>
        public DbCommand Command{get;private set;}

        /// <summary>
        /// 数据库连接当前是否是处于已打开状态
        /// </summary>
        public bool ConnectionIsOpened{get;private set;}

        /// <summary>
        /// 是否保持数据连接的打开状态。如果值为true则数据源被打开后不自动关闭。
        /// 否则运行完语句后自动恢复数据连接的原状(如果原连接是关闭状态则运行完语句后关闭连接；如果原连接是打开状态则运行完语句后保持打开状态）
        /// </summary>
        public bool KeepConnection { get; private set; }

        /// <summary>
        /// 开始执行
        /// </summary>
        protected void BeginExecute()
        {
            if (this.Command.Connection.State == ConnectionState.Closed)
            {
                this.ConnectionIsOpened = false;
                this.Command.Connection.Open();
            }
        }
        /// <summary>
        /// 执行已结束
        /// </summary>
        /// <param name="revert">是否恢复连接的原状</param>
        private void EndExecute(bool revert)
        {
            this.Command.Parameters.Clear();
            if (revert)
            {
                //如果原非处于已打开状态且当前是已打开状态，且不保持打开状态则关闭连接。
                if (!this.ConnectionIsOpened
                    && !this.KeepConnection)
                {
                    if (this.Command.Connection.State != ConnectionState.Closed)
                        this.Command.Connection.Close();
                }
            }
        }

        /// <summary>
        /// 执行SQL语句并返回受影响行数
        /// </summary>
        /// <returns></returns>
        public int ExecuteNonQuery()
        {
            this.BeginExecute();
            var r = this.Command.ExecuteNonQuery();
            this.EndExecute(true);
            return r;
        }
        /// <summary>
        /// 针对 System.Data.Common.DbCommand.Connection 执行 System.Data.Common.DbCommand.CommandText，并返回System.Data.Common.DbDataReader。
        /// </summary>
        /// <returns></returns>
        public DbDataReader ExecuteReader()
        {
            this.BeginExecute();
            CommandBehavior cb = CommandBehavior.Default;
            if(!this.ConnectionIsOpened && !this.KeepConnection)
                cb = CommandBehavior.CloseConnection;

            var r = this.Command.ExecuteReader(cb);

            this.EndExecute(false);

            return r;
        }
        /// <summary>
        /// 执行查询，并返回一个数据对象列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public List<T> ExecuteDbObjectList<T>()
            where T : DbObject, new()
        {
            using (var reader = this.ExecuteReader())
            {
                return reader.ToDbObjectList<T>();
            }
        }
        /// <summary>
        /// 执行查询，并返回一个数据对象列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="startIndex">起始行数, 从0开始计算</param>
        /// <param name="count">获取数量</param>
        /// <returns></returns>
        public List<T> ExecuteDbObjectList<T>(int startIndex, int count)
            where T : DbObject, new()
        {
            int num = 0;
            int offset = 0;
            using (var reader = this.ExecuteReader())
            {
                List<T> list = new List<T>(count);
                while (num < count && reader.Read())
                {
                    if (offset >= startIndex)
                    {
                        list.Add(reader.ToDbObject<T>());
                        num++;
                    }
                    offset++;
                }
                return list;
            }
        }
        /// <summary>
        /// 执行查询，并返回查询所返回的结果集中第一行的第一列。所有其他的列和行将被忽略。
        /// </summary>
        /// <returns></returns>
        public object ExecuteScalar()
        {
            this.BeginExecute();
            object r = this.Command.ExecuteScalar();
            this.EndExecute(true);

            return r;
        }

        /// <summary>
        /// 执行命令语句，并填充DataSet内存表
        /// </summary>
        /// <param name="adapter"></param>
        /// <param name="ds"></param>
        /// <returns></returns>
        public void FillDataSet(DbDataAdapter adapter, DataSet ds)
        {
            this.BeginExecute();
            adapter.SelectCommand = this.Command;
            adapter.Fill(ds);
            this.EndExecute(true);
        }

        #region IDisposable 成员
        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            if (this.Command != null)
            {
                this.Command.Parameters.Clear();
                this.Command.Dispose();
                this.Command = null;
            }
        }

        #endregion

        #region 生成已封装的数据命令
        /// <summary>
        /// 获取新的已封装的数据命令
        /// </summary>
        /// <param name="connection">数据连接</param>
        /// <param name="commandText">命令语句</param>
        /// <returns></returns>
        public static DbCommandWrapped Create(DbConnection connection, string commandText)
        {
            return Create(connection, false, CommandType.Text, commandText, null);
        }
        /// <summary>
        /// 获取新的已封装的数据命令
        /// </summary>
        /// <param name="connection">数据连接</param>    
        /// <param name="keepConnection">是否保持数据连接的打开状态。如果true则打开后不会自动关闭。
        /// 否则运行完语句后自动恢复原状(如果原连接是关闭状态则运行完语句后关闭连接；如果原连接是打开状态则运行完语句后保持打开状态）</param>
        /// <param name="commandText">命令语句</param>
        /// <returns></returns>
        public static DbCommandWrapped Create(DbConnection connection, bool keepConnection, string commandText)
        {
            return Create(connection, keepConnection, CommandType.Text, commandText,  null);
        }
        /// <summary>
        /// 获取新的已封装的数据命令
        /// </summary>
        /// <param name="connection">数据连接</param>
        /// <param name="commandText">命令语句</param>
        /// <param name="parameters">相关参数</param>
        /// <returns></returns>
        public static DbCommandWrapped Create(DbConnection connection, string commandText, params DbParameter[] parameters)
        {
            return Create(connection, false, CommandType.Text, commandText, parameters);
        }
        /// <summary>
        /// 获取新的已封装的数据命令
        /// </summary>
        /// <param name="connection">数据连接</param>    
        /// <param name="keepConnection">是否保持数据连接的打开状态。如果true则打开后不会自动关闭。
        /// 否则运行完语句后自动恢复原状(如果原连接是关闭状态则运行完语句后关闭连接；如果原连接是打开状态则运行完语句后保持打开状态）</param>
        /// <param name="commandText">命令语句</param>
        /// <param name="parameters">相关参数</param>
        /// <returns></returns>
        public static DbCommandWrapped Create(DbConnection connection, bool keepConnection, string commandText, params DbParameter[] parameters)
        {
            return Create(connection, keepConnection, CommandType.Text, commandText, parameters);
        }
        /// <summary>
        /// 获取新的已封装的数据命令
        /// </summary>
        /// <param name="connection">数据连接</param>
        /// <param name="commandType">命令类型</param>
        /// <param name="commandText">命令语句</param>
        /// <param name="parameters">相关参数</param>
        /// <returns></returns>
        public static DbCommandWrapped Create(DbConnection connection, CommandType commandType, string commandText, params DbParameter[] parameters)
        {
            return Create(connection, false, commandType, commandText, parameters);
        }
        /// <summary>
        /// 获取新的已封装的数据命令
        /// </summary>
        /// <param name="connection">数据连接</param>
        /// <param name="keepConnection">是否保持数据连接的打开状态。如果true则打开后不会自动关闭。
        /// 否则运行完语句后自动恢复原状(如果原连接是关闭状态则运行完语句后关闭连接；如果原连接是打开状态则运行完语句后保持打开状态）</param>
        /// <param name="commandType">命令类型</param>
        /// <param name="commandText">命令语句</param>
        /// <param name="parameters">相关参数</param>
        /// <returns></returns>
        public static DbCommandWrapped Create(DbConnection connection, bool keepConnection, CommandType commandType, string commandText, params DbParameter[] parameters)
        {
            DbCommand command = connection.CreateCommand();
            command.CommandType = commandType;
            command.CommandText = commandText;
            if (connection.ConnectionTimeout > command.CommandTimeout)
                command.CommandTimeout = connection.ConnectionTimeout;
            if (parameters != null)
            {
                foreach (DbParameter par in parameters)
                {
                    if(par != null)
                        command.Parameters.Add(par);
                }
            }
            return new DbCommandWrapped(command, keepConnection);
        }

        /// <summary>
        /// 获取新的已封装的数据命令
        /// </summary>
        /// <param name="command">数据命令</param>
        /// <param name="keepConnection">是否保持数据连接的打开状态。如果true则打开后不会自动关闭。
        /// 否则运行完语句后自动恢复原状(如果原连接是关闭状态则运行完语句后关闭连接；如果原连接是打开状态则运行完语句后保持打开状态）</param>
        /// <returns></returns>
        public static DbCommandWrapped Create(DbCommand command, bool keepConnection)
        {
            return new DbCommandWrapped(command, keepConnection);
        }
        #endregion
    }
}
